﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace PacketRush.Core.Test
{
    [TestClass]
    public class ReliableUdpReceiveAlgorithmTest
    {
        public uint _NextReliablePacketId = 1;
        private Dictionary<uint, uint> _ExpectingPacketIdList = new Dictionary<uint, uint>(10);

        [TestMethod]
        public void TestAlgorithm()
        {
            PacketWriter writer = new PacketWriter();
            Packet packet1 = writer.ToPacket(PacketFlags.Reliable); packet1.SequenceIndex = 1;
            Packet packet2 = writer.ToPacket(PacketFlags.Reliable); packet2.SequenceIndex = 2;
            Packet packet3 = writer.ToPacket(PacketFlags.Reliable); packet3.SequenceIndex = 3;
            Packet packet4 = writer.ToPacket(PacketFlags.Reliable); packet4.SequenceIndex = 4;
            Packet packet5 = writer.ToPacket(PacketFlags.Reliable); packet5.SequenceIndex = 5;
            Packet packet6 = writer.ToPacket(PacketFlags.Reliable); packet6.SequenceIndex = 6;
            Packet packet7 = writer.ToPacket(PacketFlags.Reliable); packet7.SequenceIndex = 7;
            Packet packet8 = writer.ToPacket(PacketFlags.Reliable); packet8.SequenceIndex = 8;
            Packet packet9 = writer.ToPacket(PacketFlags.Reliable); packet9.SequenceIndex = 9;
            Packet packet10 = writer.ToPacket(PacketFlags.Reliable); packet10.SequenceIndex = 10;

            ProcessReceivedReliablePacket(packet1);
            ProcessReceivedReliablePacket(packet2);
            ProcessReceivedReliablePacket(packet3);
            ProcessReceivedReliablePacket(packet4);
            ProcessReceivedReliablePacket(packet6);

            Assert.IsTrue(_ExpectingPacketIdList.Count == 1);
            Assert.IsTrue(_ExpectingPacketIdList.ContainsKey(5));

            ProcessReceivedReliablePacket(packet5);
            ProcessReceivedReliablePacket(packet7);
            ProcessReceivedReliablePacket(packet10);

            Assert.IsTrue(_ExpectingPacketIdList.Count == 2);
            Assert.IsTrue(_ExpectingPacketIdList.ContainsKey(8));
            Assert.IsTrue(_ExpectingPacketIdList.ContainsKey(9));

            ProcessReceivedReliablePacket(packet8);
            ProcessReceivedReliablePacket(packet9);
            ProcessReceivedReliablePacket(packet8);
            Assert.IsTrue(_ExpectingPacketIdList.Count == 0);

            ProcessReceivedReliablePacket(packet10);
            Assert.IsTrue(_ExpectingPacketIdList.Count == 0);

        }

        private void ProcessReceivedReliablePacket(byte[] rawPacket)
        {
            uint packetId = Packet.ReadSequenceIndex(rawPacket);

            if (packetId < _NextReliablePacketId)
            {
                if (ExpectingPacket(packetId))
                {
                    // we are expecting this one, so send ack and remove it
                    // from the expecting list, then pass it up to the application
                    SendAck(packetId);
                    RemoveExpecting(packetId);
                    PassToApp(rawPacket);
                }
                else
                {
                    // Must be a dupe, send ack anyway but do nothing
                    SendAck(packetId);
                }

                return;

            }

            if (packetId == _NextReliablePacketId)
            {
                // This is the next packet we are expecting, send an ack
                // and increment to the next expected packet id, then
                // pass the packet up to the application
                SendAck(packetId);
                _NextReliablePacketId++;
                PassToApp(rawPacket);
                return;
            }

            if (packetId > _NextReliablePacketId)
            {
                // we have recieved a packet thats greater than the next expected
                // packet id, this means that we are possibly missing some packets
                // inbetween

                // Add each packet between to the expecting list
                uint expected = _NextReliablePacketId;
                while (expected < packetId)
                {
                    AddExpecting(expected);
                    expected++;
                }

                // set the next reliable packet id to the packetId + 1
                _NextReliablePacketId = packetId + 1;

                // Send an ack for this packet and pass it up to the application
                SendAck(packetId);
                PassToApp(rawPacket);
                return;
            }
        }

        private void PassToApp(byte[] rawPacket)
        {
        }

        private void AddExpecting(uint expected)
        {
            _ExpectingPacketIdList.Add(expected, expected);
        }

        private void RemoveExpecting(uint packetId)
        {
            _ExpectingPacketIdList.Remove(packetId);
        }

        private bool ExpectingPacket(uint packetId)
        {
            return _ExpectingPacketIdList.ContainsKey(packetId);
        }

        private void SendAck(uint packetId)
        {
        }
    }
}
